using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Bamboo.Utils;

namespace Bamboo.ExeShell
{
    /// <summary>
    /// Oracle Sqlplus 执行
    /// 要求安装Oracle客户端
    /// </summary>
    public class SqlplusExecute
    {
        ICommonLog _log;

        public SqlplusExecute(ICommonLog log, string serviceName, string username, string password)
        {
            _log = log;
            this.ServiceName = serviceName;
            this.UserName = username;
            this.Password = password;
        }

        public string ServiceName { get; private set; }
        public string UserName { get; private set; }
        public string Password { get; private set; }

        /// <summary>
        /// 运行超时时间，默认为3600秒
        /// </summary>
        public int TimeoutSeconds { get; set; } = 3600;

        /// <summary>
        /// 输入文件的字符集，默认GBK
        /// </summary>
        public Encoding Encoding { get; set; } = Encoding.GetEncoding("gbk");

        /// <summary>
        /// 是否忽略错误继续运行，默认忽略
        /// </summary>
        public bool IgnoreError { get; set; } = true;
        /// <summary>
        /// sqlplus运行时的工作目录(当前目录)
        /// </summary>
        public string WorkingDirectory { get; set; } = "";

        /// <summary>
        /// 执行sql
        /// </summary>
        /// <param name="title">标题</param>
        /// <param name="sql">待执行的sql</param>
        /// <param name="localkey">分隔关键词，将文本文件分隔成若干块, 当显示错误的时候会整块显示</param>
        /// <param name="printSqlInfo">是否打印需要执行的sql</param>
        /// <returns></returns>
        public ProcessExecute RunSql(string title, string sql, string localkey = "正在执行", bool printSqlInfo = true)
        {
            string tempFilename = ProcessExecute.GetTempFile("_sqlplus", ".sql");
            try
            {
                string msg = $"[SQLPLUS] 开始运行[{title}]";
                if (printSqlInfo)
                {
                    msg += "，待执行SQL：\r\n" + sql;
                }
                _log?.LogDetail(msg);
                File.WriteAllText(tempFilename, sql + "\r\nexit", this.Encoding);
                //执行
                var cmd = new ProcessExecute();
                cmd.EncodingName = "gbk";
                //Oracle用户密码含有特殊字符使用sqlplus远程登录 使用sqlplus在windows平台登录方式：用户名/"""密码"""@服务名 
                cmd.FileName = "sqlplus.exe";
                cmd.Arguments = $"{UserName}/\"\"\"{Password}\"\"\"@{ServiceName} \"@{tempFilename}\"";
                cmd.IgnoreError = true;
                cmd.TimeoutSeconds = this.TimeoutSeconds;
                cmd.OutputHandler += _log.LogDetail;
                cmd.ErrorHandler += _log.LogDetail;
                cmd.TimeoutSeconds = TimeoutSeconds;
                cmd.MaskWords.Add($"{UserName}/\"\"\"{Password}\"\"\"");
                cmd.WorkingDirectory = WorkingDirectory;
                cmd.Run();
                //检查错误
                try
                {
                    var check = new TextFileChecker();
                    check.AddErrorKey("错误：");
                    check.AddErrorKey("错误:");
                    check.AddErrorKey("ORA-");
                    check.AddErrorKey("PLS-");
                    check.AddErrorKey("SP2-");
                    check.AddLocationKey(localkey);
                    check.AddContent("sqlplus执行结果", cmd.OutputText.ToString());
                    check.Execute();
                    _log?.LogDetail($"[SQLPLUS] =={title}== 运行成功");
                }
                catch (TextFileCheckException ex)
                {
                    _log?.LogError($"[SQLPLUS] =={title}== 运行发生错误：" + ex.ToString());
                    if (!IgnoreError) throw;
                }
                return cmd;
            }
            finally
            {
                if (File.Exists(tempFilename)) File.Delete(tempFilename);
            }
        }

        public ProcessExecute RunSql(string title, string sql)
        {
            return RunSql(title, sql, "正在执行");
        }

        public void RunSql(string title, StringBuilder sql)
        {
            RunSql(title, sql.ToString());
        }

        public void RunFile(string title, string filename)
        {
            string sql = File.ReadAllText(filename, this.Encoding);
            RunSql(title, sql);
        }
    }
}
